using System;
using System.Collections;
using System.Linq;
using System.Text.RegularExpressions;

namespace Infinity.Common.Extensions
{
	public static class StringExtension
	{
		private const string FORMAT_EXTENDED_PATTERN =
			@"(\{(?<index>\d)+((\:|\.)(?<property>[a-zA-Z]\w*))\})|(\{(?<property>[a-zA-Z]\w*)\})";

		private const string PROPERTY_NOT_EXISTS = "Property doesn't exist!";
		private const string PROPERTY_CANNOT_BE_BLANK = "Property can't be blank!";

		/// <summary>
		/// Reduce a string and append a suffix at end of the string.
		/// </summary>							 
		public static string Reduce(this string text, int count, string suffix = "...")
		{
			if (count < suffix.Length)
				throw new Exception("Too long the suffix length!");

			if (text.Length < count)
				return text;

			var reducedText = text.Substring(0, count - suffix.Length) + suffix;
			return reducedText;
		}

		/// <summary>
		/// Extended version of original String.Format function. It supports named arguments.
		/// It uses System.Reflection to resolve properties.
		/// </summary>
		/// <example>
		/// "Age: {1:Age} Name: {1:Name}, Address: {address}"
		/// </example>
		public static string FormatExtended(this string text, params object[] args)
		{
			var regex = new Regex(FORMAT_EXTENDED_PATTERN);
			var matchList = new ArrayList(regex.Matches(text)).OfType<Match>().ToArray();

			var formattedString = text;

			if (matchList.Any())
			{
				foreach (Match match in matchList.Reverse())
				{
					var groups = match.Groups;

					var groupProperty = groups["property"];
					var groupIndex = groups["index"];

					if (groupProperty.Success)
					{
						var index = -1;
						if (groupIndex.Success)
						{
							if (Int32.TryParse(groupIndex.Value, out index))
							{
								if (!index.Between(0, args.Length - 1))
								{
									throw new IndexOutOfRangeException();
								}
							}
						}
						else
							index = 0;

						if (!string.IsNullOrWhiteSpace(groupProperty.Value))
						{
							Type argType = args[index].GetType();
							var propertyInfo = argType.GetProperty(groupProperty.Value);

							if (propertyInfo.IsNotNull())
							{
								formattedString = formattedString.Remove(match.Index, match.Length)
									.Insert(match.Index, propertyInfo.GetValue(args[index], null).ToString());
							}
							else
							{
								throw new Exception(PROPERTY_NOT_EXISTS);
							}
						}
						else
						{
							throw new Exception(PROPERTY_CANNOT_BE_BLANK);
						}
					}
				}
			}

			return formattedString;
		}
	}
}